﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using PasteCodeTaskBase;
using PasteTimer.slavemodels;
using Volo.Abp.DependencyInjection;
using Z.EntityFramework.Plus;

namespace PasteTimer
{

    /// <summary>
    /// 
    /// </summary>
    public class SlaveHelper : ISingletonDependency
    {
        private SlaveMaster _master;

        private SlaveMaster _myinfo;

        private readonly ILogger<SlaveHelper> _logger;

        private readonly IHttpClientFactory _httpClientFactory;

        private readonly ChannelHelper _channelHelper;

        private TaskNodeConfig _nodeconfig;

        private readonly TaskConfig _config;

        //private readonly IAppCache _cache;

        private readonly IServiceProvider _serviceProvider;

        /// <summary>
        /// 寻找密钥，用于确定哪个地址是自己的
        /// </summary>
        private string _temptoken = String.Empty;

        /// <summary>
        /// 
        /// </summary>
        private List<SlaveInfoDto> _slavelist;

        /// <summary>
        /// 最后交互时间 时间戳秒
        /// </summary>
        private long lasttime = 0;


        /// <summary>
        /// 
        /// </summary>
        public SlaveHelper(
            ILogger<SlaveHelper> logger,
            IHttpClientFactory httpClientFactory,
            IOptions<TaskNodeConfig> config,
            IServiceProvider serviceProvider,
            ChannelHelper chennel,
            IOptions<TaskConfig> mconfig
            )
        {
            _temptoken = Guid.NewGuid().ToString().Replace("-", "").ToLower();
            _logger = logger;
            _httpClientFactory = httpClientFactory;
            _master = null;
            _myinfo = null;
            _channelHelper = chennel;
            _nodeconfig = config.Value;
            _config = mconfig.Value;
            //_cache = cache;
            _serviceProvider = serviceProvider;
        }

        /// <summary>
        /// 我的节点ID
        /// </summary>
        public int MySlaveId { get { if (_myinfo == null) { return 0; } else { return _myinfo.id; } } }
        /// <summary>
        /// 我的节点密钥
        /// </summary>
        public string MySlaveToken { get { if (_myinfo == null) { return ""; } else { return _myinfo.token; } } }

        /// <summary>
        /// 获取管理者的信息
        /// </summary>
        public SlaveMaster Master { get { return _master; } }

        /// <summary>
        /// 获取自己的信息
        /// </summary>
        public SlaveMaster MyMaster { get { return _myinfo; } }

        /// <summary>
        /// 自己是否是管理员
        /// </summary>
        public bool IsMaster
        {
            get
            {
                if (_master != null)
                {
                    if (_myinfo != null)
                    {
                        if (_myinfo.id == _master.id)
                        {
                            return true;
                        }
                    }
                }
                return false;
            }
        }

        /// <summary>
        /// 是否确认了自己的节点
        /// </summary>
        public bool IsFind
        {
            get
            {
                if (_myinfo != null)
                {
                    if (_myinfo.id != 0)
                    {
                        return true;
                    }
                }
                return false;
            }
        }

        /// <summary>
        /// 寻找密钥
        /// </summary>
        public String FindGuid { get { return _temptoken; } }

        /// <summary>
        /// 设置管理员
        /// </summary>
        /// <param name="input"></param>
        public void SetMaster(SlaveMaster input)
        {

            _master = input;
            if (input != null)
            {
                //有人成为master了 看看是否是自己
                if (IsMaster)
                {
                    _channelHelper.WriteSlaveAction(new SlaveEventModel() { Event = "startmaster", Object = "" });
                }
                else
                {
                    _channelHelper.WriteSlaveAction(new SlaveEventModel() { Event = "stopmaster", Object = "" });
                }
            }
            if (_master != null)
            {
                _nodeconfig.MasterUrl = _master.url;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<SlaveCallBack> VoteCompare(SlaveMaster input)
        {
            if (_master == null)
            {
                //直接让对方当选
                SetMaster(input);
                return new SlaveCallBack();
            }
            else
            {
                //抛弃自己现有的，使用对方的
                if (input.time < _master.time)
                {
                    SetMaster(input);
                }
                else
                {
                    //旧的是否可用
                    var result = await Link(_master);
                    if (result.code == 200)
                    {
                        //旧的还能用
                        return new SlaveCallBack()
                        {
                            code = 213,
                            message = Newtonsoft.Json.JsonConvert.SerializeObject(_master)
                        };
                    }
                    else
                    {
                        //旧的不能用了
                        SetMaster(input);
                    }
                }
            }
            return new SlaveCallBack();
        }

        /// <summary>
        /// 链接到某一个节点
        /// </summary>
        /// <param name="to"></param>
        /// <returns></returns>
        public async Task<(int code, string message)> Link(SlaveMaster to)
        {
            if (to != null && _myinfo != null)
            {
                var time = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                var temptoken = $"{time}_{_myinfo.id}_{to.id}_{_myinfo.token}_{to.token}".ToMd5Lower();
                var xtoken = $"{time}_{_myinfo.id}_{to.id}_{temptoken}";
                var result = await post($"{to.url}api/task/slave/link", "", xtoken);
                return result;
            }
            else
            {
                return (500, "信息不全，无法进行操作！");
            }
        }

        /// <summary>
        /// 健康检查，往master检查
        /// </summary>
        /// <returns></returns>
        public async Task<(int code, string message)> Health()
        {
            var nowminsec = DateTimeOffset.Now.AddSeconds(-60).ToUnixTimeMilliseconds();
            if (nowminsec > lasttime)
            {
                if (_master != null && _myinfo != null)
                {
                    if (!IsMaster)
                    {
                        var time = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                        var temptoken = $"{time}_{_myinfo.id}_{_master.id}_{_myinfo.token}_{_master.token}".ToMd5Lower();
                        var xtoken = $"{time}_{_myinfo.id}_{_master.id}_{temptoken}";
                        var postobj = new SlaveMaster()
                        {
                            id = _myinfo.id,
                            time = _myinfo.time,
                            url = _myinfo.url
                        };
                        var result = await post($"{_master.url}api/task/slave/health", Newtonsoft.Json.JsonConvert.SerializeObject(postobj), xtoken);
                        if (result.code == 200)
                        {
                            lasttime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                            //如果master变更了，则接受新的
                            var call = Newtonsoft.Json.JsonConvert.DeserializeObject<SlaveCallBack>(result.message);
                            if (call != null && call != default)
                            {
                                if (call.code == 213)
                                {
                                    var master = Newtonsoft.Json.JsonConvert.DeserializeObject<SlaveMaster>(result.message);
                                    if (master != null)
                                    {
                                        SetMaster(master);
                                        //更换了新的master
                                    }
                                }
                            }
                        }
                        return result;
                    }
                    else
                    {
                        return (200, "检查成功，自己为master,直接默认成功！");
                    }
                }
                else
                {
                    return (500, "信息不全，无法进行操作！");
                }
            }
            return (200, "时限内有交互，不需要心跳包");
        }

        /// <summary>
        /// slave 中把事件信息发送给master
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<(int code, string message)> PostAction(SlaveEventModel input)
        {
            if (_master != null && _myinfo != null)
            {
                var time = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                var temptoken = $"{time}_{_myinfo.id}_{_master.id}_{_myinfo.token}_{_master.token}".ToMd5Lower();
                var xtoken = $"{time}_{_myinfo.id}_{_master.id}_{temptoken}";
                var result = await post($"{_master.url}api/task/slave/action", Newtonsoft.Json.JsonConvert.SerializeObject(input), xtoken);
                if (result.code == 200)
                {
                    lasttime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                }
                return result;
            }
            else
            {
                return (500, "信息不全，无法进行操作！");
            }
        }

        /// <summary>
        /// 推送消息
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        public async Task<(int code, string message)> PostNotify(noticemodels.NoticeLogAddDto input)
        {
            if (_master != null && _myinfo != null)
            {
                var time = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                var temptoken = $"{time}_{_myinfo.id}_{_master.id}_{_myinfo.token}_{_master.token}".ToMd5Lower();
                var xtoken = $"{time}_{_myinfo.id}_{_master.id}_{temptoken}";
                var result = await post($"{_master.url}api/task/slave/notifiy", Newtonsoft.Json.JsonConvert.SerializeObject(input), xtoken);
                if (result.code == 200)
                {
                    lasttime = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                }
                return result;
            }
            else
            {
                return (500, "信息不全，无法进行操作！");
            }
        }

        /// <summary>
        /// master提交离线通知给slave
        /// </summary>
        /// <returns></returns>
        public async Task<(int code, string message)> PostOffline(SlaveMaster postto)
        {
            if (postto != null && _myinfo != null)
            {
                var time = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                var temptoken = $"{time}_{_myinfo.id}_{postto.id}_{_myinfo.token}_{postto.token}".ToMd5Lower();
                var xtoken = $"{time}_{_myinfo.id}_{postto.id}_{temptoken}";
                var result = await post($"{postto.url}api/task/slave/offline", "", xtoken);
                return result;
            }
            else
            {
                return (500, "信息不全，无法进行操作！");
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="url"></param>
        /// <param name="body"></param>
        /// <param name="xtoken"></param>
        /// <returns></returns>
        private async Task<(int code, string message)> post(string url, string body, string xtoken)
        {
            try
            {
                using HttpContent httpcontent = new StringContent(body);
                var timestmp = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                httpcontent.Headers.Add("xtoken", xtoken);
                httpcontent.Headers.Add("stoken", _config.PublicToken);
                httpcontent.Headers.ContentType = System.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json;charset=utf-8");
                var client = _httpClientFactory.CreateClient();
                client.Timeout = TimeSpan.FromSeconds(3);
                using var response = await client.PostAsync(url, httpcontent);
                var responseBody = await response.Content.ReadAsStringAsync();
                if (response.IsSuccessStatusCode)
                {
                    return (200, responseBody);
                }
                else
                {
                    return (500, responseBody);
                }
            }
            catch (Exception exl)
            {
                _logger.LogException(exl);
                return (500, exl.Message);
            }
        }

        /// <summary>
        /// 启动竞选master，可能中途会被取消，要看谁先
        /// </summary>
        /// <returns></returns>
        public async Task<int> StartVote(List<SlaveInfoDto> nodes)
        {
            _slavelist = nodes;
            //如果这个过程中，有人来选举了... .. . 咋处理？
            SetMaster(null);
            var my = new SlaveMaster()
            {
                id = MySlaveId,
                time = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
                token = _myinfo.token,
                url = _myinfo.url
            };
            var boolbreak = false;
            foreach (var item in nodes)
            {
                if (Master == null || Master.time > my.time)
                {
                    var time = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                    var temptoken = $"{time}_{_myinfo.id}_{item.Id}_{_myinfo.token}_{item.Token}".ToMd5Lower();
                    var xtoken = $"{time}_{_myinfo.id}_{item.Id}_{temptoken}";
                    var result = await post($"{item.Url}api/task/slave/vote", Newtonsoft.Json.JsonConvert.SerializeObject(my), xtoken);
                    Console.WriteLine($"{DateTime.Now} to vote url:{item.Url}");
                    Console.WriteLine($"code:{result.code} message:{result.message}");

                    if (result.code == 200)
                    {
                        var info = Newtonsoft.Json.JsonConvert.DeserializeObject<SlaveCallBack>(result.message);
                        if (info != null && info != default)
                        {
                            if (info.code == 213)
                            {
                                var slove = Newtonsoft.Json.JsonConvert.DeserializeObject<SlaveMaster>(info.message);
                                if (slove != null && slove != default)
                                {
                                    SetMaster(slove);
                                }
                                boolbreak = true;
                                break;
                            }
                        }
                    }
                    else
                    {
                        //请求失败了
                        _logger.LogError($"{DateTime.Now} foreach to vote master error! code:{result.code} message:{result.message} url:{item.Url}");
                    }
                }
                else
                {
                    boolbreak = true;
                    //有人捷足先登了
                    break;
                }
            }
            if (!boolbreak)
            {
                //自己成为master了
                SetMaster(my);
            }
            return 1;
        }

        /// <summary>
        /// 更新时间戳
        /// </summary>
        /// <param name="id"></param>
        public bool SlaveUpdateTime(int id)
        {
            if (_slavelist != null)
            {
                var find = _slavelist.Where(x => x.Id == id).FirstOrDefault();
                if (find != null && find != default)
                {
                    find.time = DateTimeOffset.Now.ToUnixTimeMilliseconds();
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// 把子节点加入到集合中
        /// </summary>
        /// <param name="input"></param>
        public void SlaveAppend(SlaveMaster input)
        {
            var find = _slavelist.Where(x => x.Id == input.id).FirstOrDefault();
            if (find == null || find == default)
            {
                _slavelist.Add(new SlaveInfoDto()
                {
                    Id = input.id,
                    IsEnable = true,
                    IsMaster = false,
                    SlaveState = SlaveState.online,
                    time = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
                    Token = input.token,
                    Url = input.url
                });
            }
        }

        /// <summary>
        /// 找到自己
        /// </summary>
        /// <param name="nodes"></param>
        /// <returns></returns>
        public async Task<int> FindMe(List<SlaveInfoDto> nodes)
        {
            foreach (var item in nodes)
            {
                var result = await post($"{item.Url}api/task/slave/find?guid=" + FindGuid, "", "");
                if (result.code == 200)
                {
                    if (result.message == "200")
                    {
                        _myinfo = new SlaveMaster()
                        {
                            id = item.Id,
                            time = DateTimeOffset.Now.ToUnixTimeMilliseconds(),
                            token = item.Token,
                            url = item.Url
                        };
                        _nodeconfig.Group = "slave";
                        _nodeconfig.JoinToken = item.JoinToken;
                        _nodeconfig.Url = item.Url;
                        using var _scope = _serviceProvider.CreateScope();
                        using var _dbContext = _scope.ServiceProvider.GetRequiredService<IPasteTimerDbContext>();
                        await _dbContext.NodeInfo.Where(x => x.Id == item.Id).UpdateAsync(x => new taskmodels.NodeInfo() { Status = NodeStatus.running });
                        break;
                    }
                }
            }
            return 1;
        }


        /// <summary>
        /// 检查超时
        /// </summary>
        public void FindTimeOutList()
        {
            if (_slavelist != null && _slavelist.Count > 0)
            {
                var before = DateTimeOffset.Now.AddSeconds(-65).ToUnixTimeMilliseconds();
                var timeoutlist = _slavelist.Where(x => x.time < before).ToList();
                foreach (var item in timeoutlist)
                {
                    Console.WriteLine($"[health].timeout id:{item.Id} url:{item.Url}");
                }
            }
        }
    }
}
